package src

import (
	"github.com/sirupsen/logrus"
	"strings"
)

// Hub maintains the set of active clients and broadcasts messages to the
// clients.
type Hub struct {
	// Inbound messages from the clients.
	broadcast chan []byte

	// Register requests from the clients.
	connect chan *Client

	// Unregister requests from clients.
	disconnect chan *Client
	// 要加入的房间
	join chan ClientRoomPair
	// 要离开的房间
	leave chan ClientRoomPair
	// 注册客户端身份
	register chan ClientRoomPair
	// 房间管理器
	roomManager *RoomManager
	// 客户端管理器
	clientManager *ClientManager
}

func NewHub() *Hub {
	return &Hub{
		broadcast:     make(chan []byte),
		connect:       make(chan *Client),
		disconnect:    make(chan *Client),
		join:          make(chan ClientRoomPair),
		leave:         make(chan ClientRoomPair),
		register:      make(chan ClientRoomPair),
		roomManager:   NewRoomManager(),
		clientManager: NewClientManager(),
	}
}

func (h *Hub) Run() {
	defer func() {
		if err := recover(); err != nil {
			logrus.WithFields(logrus.Fields{"location": "hub", "method": "Run"}).Warn(err)
		}
	}()
	for {
		select {
		case client := <-h.connect:
			// 将客户端加入客户端管理器
			h.clientManager.Add(client)
			client.send <- []byte(client.id)
			logrus.Debug("[hub.connect] Client connected: ", len(h.clientManager.All()), " rooms: ", len(h.roomManager.All()), " registered users: ", len(h.clientManager.AllRegistered()))

			// 连接已建立，报告连接活动
			go Report(h, "connect", client.id, client.userId, client.attach)
		case client := <-h.disconnect:
			if _, ok := h.clientManager.clients.Load(client.id); ok {
				h.Disconnect(client)
			}

			logrus.Debug("[hub.disconnect] Client connected: ", len(h.clientManager.All()), " rooms: ", len(h.roomManager.All()), " registered users: ", len(h.clientManager.AllRegistered()))
		case message := <-h.broadcast:
			for _, client := range h.clientManager.All() {
				select {
				case client.send <- message:
				default:
					h.Disconnect(client)
				}
			}
			// 收到加入房间请求
		case pair := <-h.join:
			logrus.Debug("[hub.leave] Will join room: ", pair.roomId)
			var r *Room
			if !h.roomManager.Has(pair.roomId) {
				r = NewRoom(pair.roomId)
				h.roomManager.Add(r)
			} else {
				r = h.roomManager.Pick(pair.roomId)
			}
			h.clientManager.Join(pair.client, r)
			logrus.Debug("[hub.join] Client connected: ", len(h.clientManager.All()), " rooms: ", len(h.roomManager.All()), " registered users: ", len(h.clientManager.AllRegistered()))
			// 收到离开房间请求
		case pair := <-h.leave:
			logrus.Debug("[hub.leave] Will leave room: ", pair.roomId)
			if h.roomManager.Has(pair.roomId) {
				h.clientManager.Leave(pair.client, pair.roomId)
				logrus.Debug("[hub.leave] Client connected: ", len(h.clientManager.All()), " rooms: ", len(h.roomManager.All()), " registered users: ", len(h.clientManager.AllRegistered()))
			}
		case pair := <-h.register:
			h.Register(pair.client, pair.roomId)
		}
	}
}

// Push 向指定房间推送消息
func (h *Hub) Push(to string, tp string, message []byte) {
	logrus.WithFields(logrus.Fields{"method": "Push"}).Info("type: " + tp + " to: " + to + " message: " + string(message))
	if tp == TypeRoom {
		h.roomManager.Push(to, message)
	} else if tp == TypePersonal {
		h.clientManager.Push(to, message)
	} else {
		// 给一组用户发送消息
		group := strings.Split(to, ",")
		for _, t := range group {
			h.clientManager.Push(t, message)
		}
	}
}

func (h *Hub) Disconnect(client *Client) {
	close(client.send)
	close(client.readPumpStopped)
	close(client.writePumpStopped)
	// 连接已断开，报告连接活动
	go Report(h, "disconnect", client.id, client.userId, client.attach)
	// 退出所有房间
	h.clientManager.LeaveAll(client)
	// 从客户端管理器中移除该客户端
	h.clientManager.Remove(client)
	// 注销用户
	h.clientManager.UnRegister(client)
	close(client.closed) // 这也会发送一个消息过去
}

// Register 注册客户端身份
func (h *Hub) Register(client *Client, userId string) {
	logrus.Debug("[hub.register] Will register user as: ", userId)
	err := h.clientManager.Register(client, userId)
	if err != nil {
		logrus.Info("[hub.register] User id conflicts, disconnect")
		client.send <- []byte(err.Error())
		h.Disconnect(client)
		_ = client.conn.Close()
	} else {
		client.send <- []byte{'o', 'k'}
	}

	logrus.Debug("[hub.join] Client connected: ", len(h.clientManager.All()), " rooms: ", len(h.roomManager.All()), " registered users: ", len(h.clientManager.AllRegistered()))
}
